use lazy_static::lazy_static;
use regex::Regex;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::*;
use std::io::{self, BufRead};
use std::path::Path;
use std::process;
//----------------------------------
pub struct Config {
    pub filename: String,
}

impl Config {
    pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
        args.next();

        let filename = match args.next() {
            Some(arg) => arg,
            None => return Err("Didn't get a file name"),
        };

        Ok(Config { filename })
    }
}

pub fn run_parse() {
    let args: Vec<String> = env::args().collect();
    println!("{:?}", args);

    let config = Config::new(env::args()).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });

    println!("In file {}", config.filename);

    if let Err(e) = parse_mesh(config) {
        println!("Application error: {}", e);
        process::exit(1);
    }
}

//------------------------------------
fn find_start(text: &str) -> (bool, String) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(\w+) +\{").unwrap();
    };

    let re = Regex::new(r"([a-z]+)").unwrap();
    let caps_dft = re.captures("none").unwrap();

    let caps = RE.captures(text).unwrap_or(caps_dft);
    let text1 = caps.get(1).map_or("", |m| m.as_str());

    (RE.is_match(text), text1.to_string())
}

fn find_end(text: &str) -> bool {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"\}").unwrap();
    };
    RE.is_match(text)
}

fn my_replace(text: &str) -> String {
    let re = Regex::new(r"[\[\],]").unwrap();
    let after = re.replace_all(text, " ");

    let re2 = Regex::new(r"^\s+").unwrap();
    let after = re2.replace_all(&after, "");

    let re3 = Regex::new(r" +").unwrap();
    let after = re3.replace_all(&after, " ");

    after.to_string()
}

//---------------------------------------------------
fn parse_mesh(config: Config) -> Result<(), Box<dyn Error>> {
    let fname = config.filename;

    let mut tag_list: Vec<String> = Vec::new();
    let mut tag_start: Vec<i32> = Vec::new();
    let mut parse: HashMap<String, [i32; 2]> = HashMap::new();

    let mut term_ord = 0;
    // File hosts must exist in current path before this produces output
    if let Ok(lines) = read_lines(&fname) {
        let mut lnum = 0;
        // Consumes the iterator, returns an (Optional) String
        for line in lines {
            if let Ok(ip) = line {
                let (ret, ret_str) = find_start(&ip);
                if ret {
                    let term = format!("{}-{}", ret_str, term_ord);
                    term_ord += 1;
                    println!("find: {} @ {}", &term, lnum);
                    tag_list.push(term);
                    tag_start.push(lnum);
                }
                let ret = find_end(&ip);
                if ret {
                    println!("find }} that end {} @ {}", tag_list[tag_list.len() - 1], lnum);
                    parse.insert(
                        tag_list.pop().unwrap_or("error".to_string()),
                        [tag_start.pop().unwrap_or(-1), lnum],
                    );
                }
                lnum += 1
            }
        }
        // print!("{:?}", parse);
    } else {
        println!("Cant't find file {}", &fname);
    }

    // 创建一个空目录
    fs::create_dir("./result")?;

    for (node, range) in parse.iter() {
        let mut lnum = 0;
        let f = File::create(format!("./result/{node}.txt"))?;
        let mut writer = BufWriter::new(f);

        if let Ok(lines) = read_lines(&fname) {
            for line in lines {
                if let Ok(ip) = line {
                    if lnum > range[0] && lnum < range[1] {
                        let out_line = my_replace(&ip);
                        writeln!(writer, "{}", out_line)?;
                    }
                }
                lnum += 1;
            }
        } else {
            println!("Cant't find file {}", &fname);
        }
    }

    Ok(())
}

// 使用 Result 包装, 以 match 错误
//  返回 iterator,  读取文件中的行
pub fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
    P: AsRef<Path>,
{
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}
